#include <pthread.h>
#include <fcntl.h>
#include <mqueue.h>
#include "server.h"
#include "ssl.h"
#include "ecmfs_cmn.h"
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>


ecmfs_msg_queue_t *edge_mq;
int is_ready_for_sessions=0;
#define DEBUG 0

#if DEBUG
#define DEBUG_LOG(fmt , args...) \
    printf("\n[%s][%d] " fmt "\n" , __FUNCTION__, __LINE__ , ##args );
#else
#define DEBUG_LOG(fmt , args... ) 
#endif
/* Lets temporarily have 4 sessions
   in demo, we can update this to dynamic
   list later 
 */

edge_sessions_t sessions;

int create_edge_thread(void)
{
    int retval;
    int err_num;
    server_thread_info_t  *tinfo;

    edge_mq = ecmfs_message_queue_init();
    if (edge_mq == NULL) {
    	fprintf(stderr,
            "Failed to create msgq for edge thread.");
    	return ECMFS_FALSE;
    }

         tinfo = malloc(sizeof(server_thread_info_t));
	     if (!tinfo) {
	     	 fprintf(stderr, 
	             "Failed to alloc for edge thread");
		     ecmfs_message_queue_deinit(edge_mq);	         
	     	 return ECMFS_FALSE;
	     }

	     retval = pthread_create( &tinfo->thread_id,
	 			     NULL,
	 			     edge_thread,
	 			     (void*)tinfo);

	     if (retval < 0 ) {
	     	err_num = errno;
	     	fprintf(stderr, 
	             "Failed to create edge thread. errno=%d [%s]", err_num, 
	     		strerror(err_num));
 	        ecmfs_message_queue_deinit(edge_mq);	         
	        free(tinfo);
	        retval = ECMFS_FALSE;
	     } else {
	 	    DEBUG_LOG(
	         "Created edge thread. thr_idx = %d", tinfo->tid);
	 	    retval = ECMFS_TRUE;
	 	}
	 sessions.max_sessions = 4;
	 sessions.sessList = (session_aes_key_t *)
	             malloc(sizeof(session_aes_key_t));
	 if (NULL == sessions.sessList)
	 {
        fprintf(stderr, 
         "Failed to allocate session structure"); 
	 }
     return(retval);
        
}

void edge_handle_message(unsigned char *buffer, int bufsize, int *more)  
{
    unsigned char *decoded_buffer; 
    ecmfs_message_t *msg;
    int decoded_bufsize;
    
    if (bufsize < sizeof(ecmfs_message_t)) {
        fprintf (stderr, "Edge: cannot handle this message\n");
        return;
    }
        
    msg = (ecmfs_message_t *) buffer;
    if (msg->magic_num == 19977991) {
      /* Can get clear text for initial
         user add. A TODO is to make that
         an SSL connection, where the master
         does not know the user's public key
         yet.
       */
      fprintf (stderr, "Server: got cleartext\n");
    } else {
      /* Check the socket properties
         client sends data encrypted 
         in symmetric key
         Other nodes encrypt using
         self public key 
       */
      fprintf (stderr, "Server: got non ecmfs header check if encrypted\n");
      decode_rsa_message(&ecmfs_ssl, ecmfs_ssl.enc_priv_key, buffer, bufsize,
                    &decoded_buffer, &decoded_bufsize); 
      msg = (ecmfs_message_t *) decoded_buffer;
    }

    fprintf(stderr, "Message from:");    
    switch(msg->from) 
    {
     case NODE_CLIENT:
         fprintf(stderr, "Client : \n");
         edge_handle_client_message(msg, more);
         break;
     case NODE_EDGE:
         fprintf(stderr, "Edge : \n");
         edge_handle_edge_message(msg, more);
         break;
     case NODE_CORE:
         fprintf(stderr, "Core : \n");
         edge_handle_core_message(msg, more);
         break;
     case NODE_MASTER:
         fprintf(stderr, "Master : \n");
         edge_handle_master_message(msg, more);
         break;
     default:
         fprintf(stderr, "Unknown : \n");
         break;
    }
} 

void *
edge_thread (void *arg)
{
    pid_t tid, pid;
    server_thread_info_t *thread_info;
    unsigned char *buffer;    
    int bytes_read;
    int more = 0;
    int sd;
    int ret;
    
    pthread_detach (pthread_self());

    if (edge_mq == NULL) {
	    fprintf(stderr,"No queue to wait in %s. Exiting",
                     __FUNCTION__);
        pthread_exit(NULL);
    }
    

    if (!arg) {
    	fprintf(stderr,"NULL arg to %s. Exiting", __FUNCTION__);
	    pthread_exit(NULL);
    }

    //edge_signal_blocker();
    
    thread_info = (server_thread_info_t *)arg;

    tid = (pid_t)syscall(SYS_gettid);
    pid = getpid();

    thread_info->pid = pid;
    thread_info->tid = tid;
    
    while(1) {

        
        while (1) {
        if (ecmfs_message_queue_count(edge_mq) <= 0) {
            usleep(200);
        } else {
            break;
        }
        }
        
        
        ret = ecmfs_message_queue_del(edge_mq,
                    (char **) &buffer, &bytes_read, &sd); 
        if ((ret == -1) ||(bytes_read == 0))
        {
           fprintf(stderr,"Failed to recieve:");
           continue;
        } else {
            //handle the msg
            more =  sd;
            edge_handle_message(buffer,bytes_read, &more);
            free(buffer);
        }
    }
    
    pthread_exit(NULL);
}

bool_ecmfs  edge_msg_send (unsigned char *buffer, int buff_size, int sd)
{
    int ret;
    ecmfs_message_t *msg;

    DEBUG_LOG("Send %p with size %d\n", buffer, buff_size);
   
    if (edge_mq == NULL) {
        fprintf(stderr,"\n No queue handle found :%s",
            
                __FUNCTION__);
        return ECMFS_FALSE;
    }
    msg = (ecmfs_message_t *) buffer;
    DEBUG_LOG("Enqueing msg %p, msg->to=%d, type %d accept sd=%d\n", 
                msg, msg->to, msg->type, sd);
    dump_ecmfs_message(msg);

    ret = ecmfs_message_queue_add (edge_mq, (void *)buffer, 
                         buff_size,sd);
    if (ret != 0) {
        fprintf(stderr,"\n FRR : failed to process_enqueue :%s",
                __FUNCTION__);
        free(msg);
        return ECMFS_FALSE;
    }

    return ECMFS_TRUE;
}


bool_ecmfs  edge_handle_client_message(ecmfs_message_t *msg, int *more)
{
    fprintf(stderr, "%s\n", __FUNCTION__);
    dump_ecmfs_message(msg);
    return ECMFS_TRUE;
}

bool_ecmfs  edge_handle_edge_message(ecmfs_message_t *msg, int *more)
{
    fprintf(stderr, "%s\n", __FUNCTION__);
    dump_ecmfs_message(msg);
    return ECMFS_TRUE;
}

bool_ecmfs  edge_handle_core_message(ecmfs_message_t *msg, int *more)
{
    fprintf(stderr, "%s\n", __FUNCTION__);
    dump_ecmfs_message(msg);
    return ECMFS_TRUE;
}

bool_ecmfs  edge_handle_master_message(ecmfs_message_t *msg, int *more)
{
    fprintf(stderr, "%s\n", __FUNCTION__);
    dump_ecmfs_message(msg);
    
    switch(msg->type) 
    {
        case MASTER_OP_RESPONSE:
            {
                ecmfs_server_response_t *response;
                
                response = (ecmfs_server_response_t *)
                        msg->message;
                         
                switch(response->resp) 
                {
                    case SERVER_RESP_HELLO:
                        fprintf (stderr, "Server said hello\n");
                        break;
                    case SERVER_OP_SENDING_PUBKEY:
                        {
                        char keyStorePath[256];
                        int c;                            
                        ecmfs_runtime_state_t *masterState;
                        fprintf (stderr, "Server is sending its public key\n");
                        masterState = (ecmfs_runtime_state_t *) response->msg;
                        
                        memset(keyStorePath, 0, 
                               sizeof(keyStorePath));
                        strncpy(keyStorePath, ecmfs_path, 256);
                        strcat(keyStorePath, "/");    
                        c = strlen(keyStorePath);
                        strncat(keyStorePath, cfile->my_name,(256-c)?(256-c):0);
                        c = strlen(keyStorePath);
                        strcat(keyStorePath, "/");    
                        strncat(keyStorePath, masterState->info.node_name,
                                            (256-c)?(256-c):0); 
                        strncat(keyStorePath, "_pub.key",
                                        (256-c)?(256-c):0); 
                        print_runtime_state(masterState);
                        copy_to_file((char *)masterState->info.node_pub_key, 
                                    masterState->info.node_pub_key_len,
                                    keyStorePath);
                        }
                        break;                        
                    case SERVER_RESP_INTERNAL_ERROR:
                           fprintf (stderr, 
                            "Server had an internal error\n");
                        return ECMFS_TRUE;  
                    case SERVER_RESP_UNKNOWN:
                    default:
                           fprintf (stderr, 
                            "Unknown response\n");
                        return ECMFS_TRUE;                         
                }
                
                if (response->state != SERVER_STATE_READY)
                {
                   fprintf (stderr, "Server not ready for sessions,"
                       "waiting for notifications ..\n");
                   return ECMFS_TRUE;
                }
                if (*more == 0) { /* No more checks from 
                                     server. Ready for handling
                                     sessions
                                   */
                   fprintf (stderr, "Server Ready for sessions,"
                       "waiting for notifications ..\n");
                    is_ready_for_sessions = 1;
                }
            }
        case MASTER_OP_NOTIFICATION:
            {
                ecmfs_server_response_t *response;
                
                response = (ecmfs_server_response_t *)
                        msg->message;
                         
                switch(response->resp) 
                {
                    case SERVER_NOTIFY_STATUS_UPDATE:
                        {
                        char keyStorePath[256];
                        int c;                            
                        ecmfs_runtime_state_t *masterState;
                        fprintf (stderr, "Server is sending its public key\n");
                        masterState = (ecmfs_runtime_state_t *) response->msg;
                        
                        memset(keyStorePath, 0, 
                               sizeof(keyStorePath));
                        strncpy(keyStorePath, ecmfs_path, 256);
                        strcat(keyStorePath, "/");    
                        c = strlen(keyStorePath);
                        strncat(keyStorePath, cfile->my_name,(256-c)?(256-c):0);
                        c = strlen(keyStorePath);
                        strcat(keyStorePath, "/");    
                        strncat(keyStorePath, masterState->info.node_name,
                                            (256-c)?(256-c):0); 
                        strncat(keyStorePath, "_pub.key",
                                        (256-c)?(256-c):0); 
                        print_runtime_state(masterState);
                        copy_to_file((char *)masterState->info.node_pub_key, 
                                    masterState->info.node_pub_key_len,
                                    keyStorePath);
                        }
                        break;
                    case SERVER_NOTIFY_SESSION_UPDATE:
                        {
                            if (sessions.sess_count < sessions.max_sessions)
                            {
                               fprintf (stderr,"Session Details Copied\n");                                
                                memcpy(
                                   &sessions.sessList[sessions.sess_count],
                                   (session_aes_key_t *) response->msg, 
                                   sizeof(session_aes_key_t));
                                sessions.sess_count++;
                            }
                        }
                        break;                        
                        
                    default:
                           fprintf (stderr, 
                            "Unknown response\n");
                        return ECMFS_TRUE;                         
                }
                
               fprintf (stderr, "Server state = %d,",
                        response->state);
                if (response->state == SERVER_STATE_READY) {
                    fprintf (stderr, "Server Ready for sessions\n");
                    is_ready_for_sessions = 1;
                }
            }
            break;            
        case MASTER_OP_USERUPDATE:
        case MASTER_OP_NODEUPDATE:
        case MASTER_OP_SESSIONUPDATE:
        case MASTER_OP_FAILOVER:
        default:
           fprintf (stderr, "Not yet handled\n");
           return ECMFS_TRUE;   
    }
    return ECMFS_TRUE; 

}

